<?php

require_once 'DBManager.php';
require_once 'Capacity.php';
require_once 'HttpClient/HttpClient.php';
require_once 'HttpClient/HttpClientAuth.php';
require_once 'HttpClient/CloudVirtualMachine.php';
require_once 'Zend/Log.php';
require_once 'Zend/Debug.php';
require_once 'Zend/Log/Writer/Stream.php';
require_once 'Zend/Config/Ini.php';

define(CONFIGURATION_FILE, 'config/daemon.ini');
define(APPLICATION_ENV, 'development');

class ResourceControl {

    private $log;
    private $dbAccess;
    private $globalResources;
    private $usedResourcesRealTime;

    /**
     * Inicializa todos los parametros para el control de utilizacion de 
     * recursos en la plataforma.
     */
    public function __construct() {
        $config = new Zend_Config_Ini(CONFIGURATION_FILE, APPLICATION_ENV);
        $writer = new Zend_Log_Writer_Stream($config->get('resource_control_logfile'));
        $this->log = new Zend_Log($writer);
        $this->dbAccess = new DBManager($this->log);
        $this->globalResources = $this->getConfigCapacityNow();
        $this->log->debug("Global resources");
        $this->log->debug(print_r($this->globalResources, true));
        $capacity = new Capacity($this->log);
        $this->usedResourcesRealTime = $capacity->listCapacity();
        $this->log->debug("Used resources real time");
        $this->log->debug(print_r($this->usedResourcesRealTime, true));
    }

    /**
     * Recupera los datos de PERFIL_ENCENDIDO_GLOBAL para calcular si se puede mantener encendida alguna maquina.
     * 
     * @return type
     * @throws Exception
     */
    private function getGlobalResources() {
        try {
            $resources = $this->getConfigCapacityNow();
            $response = array();
            $dias = array('Do', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sa');
            foreach ($resources as $r) {
                $response[$dias[$r['dia']]] = array('inicio' => $r['hora_inicio'], 'fin' => $r['hora_fin'], 'memory' => $r['cantidad_ram'], $r['cantidad_cpu']);
            }
            return $response;
        } catch (Exception $e) {
            $this->log->err($e->getTraceAsString());
            throw $e;
        }
    }

    /**
     * Verifica maquin a maquina si se debe apagar, si es asi envia la 
     * peticion de apagado a la plataforma.
     * 
     * @throws Exception
     */
    public function checkInstancesToPowerOff() {
        try {
            $vms = $this->getTurnedOnVms();
            foreach ($vms as $vm) {
                $this->turnOff($vm);
            }
        } catch (Exception $e) {
            $this->log->err($e->getTraceAsString());
            throw $e;
        }
    }

    /**
     * 
     * Recupera los codigos de maquinas virtuales encendidas desde cloudstack.
     */
    private function getTurnedOnVms() {
        $vmCloud = new CloudVirtualMachine($this->log);
        try {
            $vms = $vmCloud->listVM();
            $this->log->debug("[ maquinas virtuales ]");
            $this->log->debug(print_r($vms, true));
            $codigos = array();
            foreach ($vms['listvirtualmachinesresponse']['virtualmachine'] as $v) {
                // sacar los codigoVm
//            print_r($v);
                if ($v['state'] === 'Running' || $v['state'] === 'Started' /* || $v['state'] === 'Starting' */)
                    $codigos[] = $v['id'];
            }
            return $codigos;
        } catch (Exception $e) {
            $this->log->err($e->getTraceAsString());
            throw $e;
        }
    }

    private function apagar($codigoVm) {
        $vm = new CloudVirtualMachine($this->log);
        $vm->apagarVM($codigoVm);
    }

    /**
     * Verifica el consumo para una maquina virtual $codigoVm para solicitar
     * el apagado a cloudstack.
     * 
     * @param type $codigoVm
     * @throws Exception
     */
    private function turnOff($codigoVm) {
        $dayWeek = array(
            'Mon' => 'Lu',
            'Tue' => 'Ma',
            'Wed' => 'Mi',
            'Thu' => 'Ju',
            'Fri' => 'Vi',
            'Sat' => 'Sa',
            'Sun' => 'Do'
        );

        $select = $this->dbAccess->getDb()->select()->from(array('pe' => 'perfil_encendido'), array('codigo_perfil_encendido', 'dias_encendido', 'hora_inicio_dia', 'hora_fin_dia', 'horas_utilizacion_diario', 'horas_utilizacion_curso', 'permite_fuera_horario'));
        $select->join(array('l' => 'laboratorio'), "l.codigo_perfil_encendido = pe.codigo_perfil_encendido", array())
                ->join(array('vm' => 'maquina_virtual'), "vm.codigo_laboratorio = l.codigo_laboratorio", array());
        $select->where("codigo_vm = ?", $codigoVm);
        $this->log->debug($select);
        try {
            $pe = $this->dbAccess->getDb()->fetchRow($select);
        } catch (Exception $e) {
            $this->log->err($e->getTraceAsString());
            throw $e;
        }

        $today = $dayWeek[date('D')];
        //verificar si permite el dia de hoy verificando si el 
        $pos = strpos($pe['dias_encendido'], $today);
        if ($pos === FALSE) {
            $this->log->debug("[ dia ] $pos");
            if (isset($this->globalResources['hora_inicio'])) {
                $hora_inicio = strtotime(date("Y-m-d") . " " . $this->globalResources['hora_inicio']);
                $hora_fin = strtotime(date("Y-m-d") . " " . $this->globalResources['hora_fin']);
                $hora_actual = strtotime(date("Y-m-d H:i:s"));
//                si se esta ejecutando dentro del rango de la configuracion global para este dia
                if (($hora_fin >= $hora_actual) && ($hora_actual >= $hora_inicio)) {
                    $cpu = floatval($this->globalResources['cantidad_cpu']);
                    $memoria = floatval($this->globalResources['cantidad_ram']);
//              si la capacidad no esta sobrepasada
                    if (($cpu > $this->usedResourcesRealTime['cpu']) && ($memoria > $this->usedResourcesRealTime['memory'])) {
                        $this->log->debug("Horario reservado. codigoVM $codigoVm");
                    }
                } else {
//                    si no, chau
                    $this->apagar($codigoVm);
                    $this->log->debug("Recursos sobrepasados. Apagar. codigoVM $codigoVm");
                }
            } else {
                //                    si no, chau
                $this->apagar($codigoVm);
                $this->log->debug("No hay configuracion de encendido global ni corresponde su dia. Apagar. codigoVM $codigoVm");
            }
        } else {
            //                es el dia correcto de encendido
            //verificar si puede prenderse en el horario..
            $hora_inicio = strtotime(date("Y-m-d") . " " . $pe['hora_inicio_dia']);
            $hora_fin = strtotime(date("Y-m-d") . " " . $pe['hora_fin_dia']);
            $hora_actual = strtotime(date("Y-m-d H:i:s"));
//            si estamos en su horario
            $this->log->debug("No existe perfil encendido");
            $this->log->debug("($hora_fin >= $hora_actual) && ($hora_actual >= $hora_inicio)");
            if (($hora_fin >= $hora_actual) && ($hora_actual >= $hora_inicio)) {
                $this->log->debug("Horario reservado. codigoVM $codigoVm");
            } else {
//no estamos en su horario, debemos buscar configuracion global.
                $this->log->debug("Vamos por configuracion global");
                if (isset($this->globalResources['hora_inicio'])) {
//                    hay configuraciones globales
                    $hora_inicio = strtotime(date("Y-m-d") . " " . $this->globalResources['hora_inicio']);
                    $hora_fin = strtotime(date("Y-m-d") . " " . $this->globalResources['hora_fin']);
                    $hora_actual = strtotime(date("Y-m-d H:i:s"));
                    if (($hora_fin >= $hora_actual) && ($hora_actual >= $hora_inicio)) {
                        $cpu = floatval($this->globalResources['cantidad_cpu']);
                        $memoria = floatval($this->globalResources['cantidad_ram']);

                        if (($cpu > $this->usedResourcesRealTime['cpu']) && ($memoria > $this->usedResourcesRealTime['memory'])) {
                            $this->log->debug("Horario reservado. codigoVM $codigoVm");
                        }
                    } else {
//                    si no, chau
                        $this->apagar($codigoVm);
                        $this->log->debug("Recursos sobrepasados. Apagar. codigoVM $codigoVm");
                    }
                } else {
//                    si no, chau
                    $this->apagar($codigoVm);
                    $this->log->debug("No hay configuracion de encendido global ni corresponde su dia. Apagar. codigoVM $codigoVm");
                }
            }
        }
    }

    /**
     * 
     * @param type $codigo 
     * @return type
     * @throws Exception
     */
    public function getPerfilEncendidoByVm($codigo) {
        try {
            $select = $this->dbAccess->select()->from(array("pe" => "perfil_encendido"), '*')
                    ->join(array("l" => "laboratorio"), "l.codigo_perfil_encendido = pe.codigo_perfil_encendido", array())
                    ->join(array("vm" => "maquina_virtual"), "vm.codigo_laboratorio = l.codigo_laboratorio", array())
                    ->where("vm.codigo_vm = ?", $codigo);

            $this->log->debug($select);
            return $this->dbAccess->getDb->fetchRow($select);
        } catch (Exception $e) {
            $this->log->err($e->getTraceAsString());
            throw $e;
        }
    }

    /**
     * Recupera la configuracion global de encendidos.
     * 
     * @return type
     * @throws Exception
     */
    public function getConfigCapacityNow() {
        try {
            $dayWeek = array(
                'Mon' => 1,
                'Tue' => 2,
                'Wed' => 3,
                'Thu' => 4,
                'Fri' => 5,
                'Sat' => 6,
                'Sun' => 7
            );

            $dia = $dayWeek[date('D')];
            $hora_actual = date("H:i:s");
            $whereSQL = "dia = $dia AND ('$hora_actual' BETWEEN hora_inicio AND hora_fin)";
            $select = $this->dbAccess->getDb()->select()->from('configuracion_encendido_global', '*')
                    ->where($whereSQL);
            $result = $this->dbAccess->getDb()->fetchRow($select);
            return $result;
        } catch (Exception $e) {
            $this->log->err($e->getTraceAsString());
            throw $e;
        }
    }

}

// main
$resourceControl = new ResourceControl();
$resourceControl->checkInstancesToPowerOff();
?>
